#ifndef OS_FIFOBUFF_H
#define OS_FIFOBUFF_H

#include <os/spinlock.h>
#include <lib/type.h>

typedef struct
{
    void *buffer;    // buffer point
    uint32_t size;   // buffer size
    uint32_t in;     // buffer input offset
    uint32_t out;    // buffer output offset
    spinlock_t lock; // buffer lock
} fifobuff_t;

#define FIFOBUFF_INIT(fifobuff, buff, size) \
    {                                       \
        .buffer = buff,                     \
        .size = size,                       \
        .in = 0,                            \
        .out = 0,                           \
        .lock = SPIN_LOCK_INIT_UNLOCK()     \
    }

void FifoBuffInit(fifobuff_t *fifobuff, void *buff, uint32_t size);
fifobuff_t *FifoBuffAlloc(uint32_t size);
void FifoBuffFree(fifobuff_t *fifobuff);
uint32_t _FifoBuffPut(fifobuff_t *fifobuff, uint8_t *buff, uint32_t len);
uint32_t _FifoBuffGet(fifobuff_t *fifobuff, uint8_t *buff, uint32_t len);

static inline uint32_t FifoBuffPut(fifobuff_t *fifobuff, uint8_t *buff, uint32_t len)
{
    uint32_t length = 0;
    SpinLock(&fifobuff->lock);
    length = _FifoBuffPut(fifobuff, buff, len);
    SpinUnlock(&fifobuff->lock);
    return length;
}

static inline uint32_t FifoBuffGet(fifobuff_t *fifobuff, uint8_t *buff, uint32_t len)
{
    uint32_t length;

    SpinLock(&fifobuff->lock);
    length = _FifoBuffGet(fifobuff, buff, len);
    SpinUnlock(&fifobuff->lock);
    return length;
}

static inline void _FifoBuffReset(fifobuff_t *fifobuff)
{
    fifobuff->in = fifobuff->out = 0;
}

static inline void FifoBuffReset(fifobuff_t *fifobuff)
{
    SpinLock(&fifobuff->lock);
    FifoBuffReset(fifobuff);
    SpinUnlock(&fifobuff->lock);
}

static inline uint32_t _FifoBuffLen(fifobuff_t *fifobuff)
{
    return fifobuff->in - fifobuff->out;
}

static inline uint32_t FifoBuffLen(fifobuff_t *fifobuff)
{
    uint32_t len;
    SpinLock(&fifobuff->lock);
    len = _FifoBuffLen(fifobuff);
    SpinUnlock(&fifobuff->lock);
    return len;
}

static inline uint32_t _FifoBuffAvali(fifobuff_t *fifobuff)
{
    return fifobuff->size - (fifobuff->in - fifobuff->out);
}

static inline uint32_t FifoBuffAvali(fifobuff_t *fifobuff)
{
    uint32_t free;
    SpinLock(&fifobuff->lock);
    free = _FifoBuffAvali(fifobuff);
    SpinUnlock(&fifobuff->lock);
    return free;
}

#endif